//
// Created by xuyan on 2023/2/20.
//

#include "filterrenderadapter.h"

FilterRenderAdapter::FilterRenderAdapter() {

}

FilterRenderAdapter::~FilterRenderAdapter() {
    deInit();
}


int FilterRenderAdapter::initAdapter(const char *logo_file,int main_width,int main_height,
                                     AVPixelFormat main_pix_fmt,AVRational main_sample_aspect_ratio,
                                     int x,int y){

    std::string filter_str = format("movie=%s[overlog];[in][overlog]overlay=%d:%d",logo_file,x,y);
    int ret = initFilter(main_width,main_height,main_pix_fmt,main_sample_aspect_ratio,filter_str.c_str());
    if(ret < 0){
       LOGE("initFilter -- failure");
       return -1;
    }

    return 0;
}

int FilterRenderAdapter::initAdapter(const char *logo_file,int main_width,int main_height,
                                         AVPixelFormat main_pix_fmt,AVRational main_sample_aspect_ratio,
                                     double offsetX,double offsetY){

    std::string filter_str = format("movie=%s[overlog];[in][overlog]overlay=main_w-overlay_w-%d:main_h-overlay_h-%d",logo_file,offsetX,offsetY);
    int ret = initFilter(main_width,main_height,main_pix_fmt,main_sample_aspect_ratio,filter_str.c_str());
    if(ret < 0){
       LOGE("initFilter -- failure");
       return -1;
    }

    return 0;

}

int FilterRenderAdapter::initAdapter(int main_width,int main_height,
                                     AVPixelFormat main_pix_fmt,AVRational main_sample_aspect_ratio,int logo_width,int logo_height,
                                     AVPixelFormat logo_pix_fmt,AVRational logo_sample_aspect_ratio, int x, int y){
    return init_filter(main_width,main_height,main_pix_fmt,main_sample_aspect_ratio,logo_width,logo_height,logo_pix_fmt,logo_sample_aspect_ratio,x,y);
}



int FilterRenderAdapter::initFilter(int main_width,int main_height,
                                    AVPixelFormat main_pix_fmt,AVRational main_sample_aspect_ratio,
                                    const char *log_ctx){
    char args[512];
    int ret = 0;
    const AVFilter *buffersrc = avfilter_get_by_name("buffer");
    const AVFilter *buffersink = avfilter_get_by_name("buffersink");
    AVFilterInOut *avfilter_outputs = avfilter_inout_alloc();
    AVFilterInOut *avfilter_inputs = avfilter_inout_alloc();
//    AVRational time_base = {1, 1000};
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };

    filter_graph = avfilter_graph_alloc();
    if (!avfilter_outputs || !avfilter_inputs || !filter_graph) {
        ret = AVERROR(ENOMEM);
        return -1;
    }

    snprintf(args, sizeof(args),
        "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
        main_width, main_height, main_pix_fmt,
        main_sample_aspect_ratio.num, main_sample_aspect_ratio.den,
        main_sample_aspect_ratio.num, main_sample_aspect_ratio.den);

    ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
        args, NULL, filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n");
        goto end;
    }

    /* buffer video sink: to terminate the filter chain. */
    ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
        NULL, NULL, filter_graph);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n");
        goto end;
    }

    ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts,
        AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n");
        goto end;
    }

    avfilter_outputs->name = av_strdup("in");
    avfilter_outputs->filter_ctx = buffersrc_ctx;
    avfilter_outputs->pad_idx = 0;
    avfilter_outputs->next = NULL;

    avfilter_inputs->name = av_strdup("out");
    avfilter_inputs->filter_ctx = buffersink_ctx;
    avfilter_inputs->pad_idx = 0;
    avfilter_inputs->next = NULL;

    if ((ret = avfilter_graph_parse_ptr(filter_graph, log_ctx,
        &avfilter_inputs, &avfilter_outputs, NULL)) < 0)
        goto end;

    if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
        goto end;

 end:
    avfilter_inout_free(&avfilter_inputs);
    avfilter_inout_free(&avfilter_outputs);

    return 0;
}

int FilterRenderAdapter::init_filter(int main_width,int main_height,
                                     AVPixelFormat main_pix_fmt,AVRational main_sample_aspect_ratio,int logo_width,int logo_height,
                                     AVPixelFormat logo_pix_fmt,AVRational logo_sample_aspect_ratio,int x, int y)
{
    int ret = 0;
    AVFilterInOut *inputs = NULL;
    AVFilterInOut *outputs = NULL;
    char filter_args[1024] = { 0 };

    filter_graph = avfilter_graph_alloc();
    if (!filter_graph) {
        printf("Error: allocate filter graph failed\n");
        return -1;
    }

    snprintf(filter_args, sizeof(filter_args),
             "buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=%d/%d[main];" // Parsed_buffer_0
             "buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=%d/%d[logo];" // Parsed_bufer_1
             "[main][logo]overlay=%d:%d[result];" // Parsed_overlay_2
             "[result]buffersink", // Parsed_buffer_sink_3
             main_width, main_height, main_pix_fmt, main_sample_aspect_ratio.num, main_sample_aspect_ratio.den,
             logo_width, logo_height, logo_pix_fmt, logo_sample_aspect_ratio.num, logo_sample_aspect_ratio.den,
             x, y);
    LOGE(filter_args);

    ret = avfilter_graph_parse2(filter_graph, filter_args, &inputs, &outputs);
    if (ret < 0) {
        printf("Cannot parse graph---%s\n",av_err2str2(ret));
        return ret;
    }

    ret = avfilter_graph_config(filter_graph, NULL);
    if (ret < 0) {
        printf("Cannot configure graph\n");
        return ret;
    }

    // Get AVFilterContext from AVFilterGraph parsing from string
    avfilter_main_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffer_0");
    avfilter_logo_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffer_1");
    avfilter_paint_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffersink_3");

    return 0;
}

int FilterRenderAdapter::onConvert(AVFrame *main_frame, AVFrame *logo_frame,AVFrame *paint_frame) {
    int ret = 0;
    int pts = main_frame->pts;
    logo_frame->pts = pts;

    ret = av_buffersrc_add_frame(avfilter_main_ctx, main_frame);      // 输入0
    if(ret < 0)
        return ret;
    ret = av_buffersrc_add_frame(avfilter_logo_ctx, logo_frame);      // 输入1
    if(ret < 0)
        return ret;
    while (true) {
        ret = av_buffersink_get_frame(avfilter_paint_ctx, paint_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            break;
        continue;
    }
    if(paint_frame){
        paint_frame->pts = pts;
    }
    return 0;
}


int FilterRenderAdapter::onConvert(AVFrame *main_frame, AVFrame **paint_frame) {
    int ret = 0;
    ret = av_buffersrc_add_frame(buffersrc_ctx, main_frame);      // 输入0
    if(ret < 0)
        return ret;
    while (true) {
        ret = av_buffersink_get_frame(buffersink_ctx, *paint_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
            break;
    }
    return 0;
}

void FilterRenderAdapter::deInit(){
    if (avfilter_main_ctx){
        avfilter_free(avfilter_main_ctx);
    }
    if (avfilter_logo_ctx){
        avfilter_free(avfilter_logo_ctx);
    }
    if (avfilter_paint_ctx){
        avfilter_free(avfilter_paint_ctx);
    }
    if(buffersrc_ctx){
       avfilter_free(buffersrc_ctx);
    }
    if(buffersink_ctx){
       avfilter_free(buffersink_ctx);
    }

    avfilter_graph_free(&filter_graph);
    avfilter_inout_free(&avfilter_inputs);
    avfilter_inout_free(&avfilter_outputs);

    free(filter_args);
}
